package v1

import (
	"context"
	"encoding/json"
	"fmt"
	"go-micro.dev/v4/client"
	"manager/lib/trace_log"
	"manager/model"
	"strings"
	"time"
	"unsafe"

	"github.com/gin-gonic/gin"
	"gitlab.local/backend/proto/dto"
	"manager/auth"
	"manager/bll"
	"manager/client/broker"
	"manager/client/grpc"
	log "manager/collector/logger"
	"manager/errors"
	"manager/event"
	"manager/model/po"
	SignalPo "manager/model/signal"
	"manager/server/web/middleware"
	"manager/store/redis"
	"manager/utils"
	"manager/utils/goroutine_pool"
)

var Signal = &signal{}

func init() {
	// 注册路由
	RegisterRouter(Signal)
}

// Signal 信号模块
type signal struct {
}

// Init 初始化路由
func (s *signal) Init(r *gin.RouterGroup) {
	// web 路由
	g := r.Group("app/signal", middleware.Auth())
	{
		g.POST("/send_cmd", s.SendCmdToDevice)
		g.POST("/toggle", s.SendToggleToDevice)
		g.POST("/send_msg", s.SendMsgToDevice)
		g.POST("/get_msg", s.GetMsgFromDevice)
		g.POST("/reactive_cmd", s.reactiveCmd)
	}
}

// SendCmdToDevice 给设备端下发指令
func (s *signal) SendCmdToDevice(c *gin.Context) {
	var (
		in  = &SignalPo.CommandReq{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	// 用户Id
	userId, _ := auth.ContextUserID(c.Request.Context())
	if userId == 0 {
		c.Error(errors.New("userid is illegal"))
		return
	}

	//  查询sn码
	sn, err := bll.Ebike.GetSn(in.EbikeId)
	if err != nil {
		c.Error(fmt.Errorf("get sn code failed"))
		return
	}

	// 检查是否正在启动
	if (in.Cmd == 0 || in.Cmd == 1) && broker.DeviceStatus.IsPowering(in.EbikeId) {
		c.Error(fmt.Errorf("车辆正在启动中"))
		return
	}

	// 检查是否启动成功
	if in.Cmd == 0 && broker.DeviceStatus.IsPowerOn(in.EbikeId) {
		c.Error(fmt.Errorf("车辆已经启动了"))
		return
	}

	if in.Cmd == 0 {
		// 发送一键启动命令通知
		event.Emit(&event.Data{
			Type: event.Event_Power_On_Cmd,
			Data: &event.BikePowerOnCmd{
				UserId: userId,
				BikeId: in.EbikeId,
			},
		})

		defer func() {
			if err != nil {
				// 添加一键启动
				trace_log.DebugLogger(sn, "SendCmdToDevice delete power key")
				key := fmt.Sprintf("%v", in.EbikeId)
				if _err := redis.KV.DelValue(c, bll.DevicePowerOnStatusKey, key); _err != nil {
					log.Errorf("deviceStatus DelValue err:%v", _err)
				}
			}
		}()
	}

	// 解锁
	if in.Cmd == int(dto.CmdType_CmdUnlock) {
		trace_log.DebugLogger(sn, "SendCmdToDevice unlock")
		// 是否超出电子围栏且已经上锁
		isLock := bll.Ebike.IsLock(in.EbikeId)
		isOverElectronicFence := bll.Setting.IsOverElectronicFence(sn, in.EbikeId)
		if isLock && isOverElectronicFence {
			trace_log.DebugLogger(sn, "SendCmdToDevice unlock over electronic fence")
			c.Error(fmt.Errorf("车辆锁定是由于超过电子围栏触发车辆锁定，请前往电子围栏处理解除"))
			return
		}
	}

	cmd := &dto.CommandRequest{
		Sn:  sn,
		Cmd: dto.CmdType(in.Cmd),
	}

	// 执行指令
	trace_log.DebugLogger(sn, "SendCmdToDevice bikeId:", in.EbikeId, " data:", utils.ToJsonString(cmd))
	ctx, cancel := context.WithTimeout(c, 15*time.Second)
	defer cancel()
	err = bll.RpcCall(sn, func(addr string, err error) error {
		if err != nil {
			return err
		}

		_, err = grpc.Cmd.Command(ctx, cmd, func(options *client.CallOptions) {
			options.Address = []string{addr}
		})
		return err
	})

	if err != nil {
		c.Error(s.FilterError(err))
		trace_log.DebugLogger(sn, "SendCmdToDevice error  bikeId:", in.EbikeId, "data:", utils.ToJsonString(cmd), " err:", err)
		return
	}

	if in.Cmd == 1 {
		// 发送一键关闭命令通知
		event.Emit(&event.Data{
			Type: event.Event_Power_Off_Cmd,
			Data: &event.BikePowerOffCmd{
				UserId: userId,
				BikeId: in.EbikeId,
			},
		})
	}

	// 发射事件
	goroutine_pool.GetPool().Push(nil, func(data interface{}) error {
		event.Emit(&event.Data{
			Type: event.Event_Bike_Cmd,
			Data: &event.BikeCmd{
				EbikeId: in.EbikeId,
				CmdType: "cmd",
				Body:    cmd,
			},
		})
		return nil
	})

	utils.ResponseOk(c, nil)
	return
}

// SendToggleToDevice 给设备端下发指令
func (s *signal) SendToggleToDevice(c *gin.Context) {
	var (
		in  = &SignalPo.ToggleReq{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	//  查询sn码
	sn, err := bll.Ebike.GetSn(in.EbikeId)
	if err != nil {
		c.Error(fmt.Errorf("get sn code failed"))
		return
	}

	// 执行指令
	in.Sn = sn
	value := (*dto.SwitchRequest)(unsafe.Pointer(uintptr(unsafe.Pointer(in)) + uintptr(8)))

	trace_log.DebugLogger(sn, "SendToggleToDevice bikeId:", in.EbikeId, " data:", utils.ToJsonString(value))
	ctx, cancel := context.WithTimeout(c, 15*time.Second)
	defer cancel()
	err = bll.RpcCall(sn, func(addr string, err error) error {
		if err != nil {
			return err
		}

		_, err = grpc.Cmd.Switch(ctx, value, func(options *client.CallOptions) {
			options.Address = []string{addr}
		})
		return err
	})

	if err != nil {
		c.Error(s.FilterError(err))
		fmt.Println("SendToggleToDevice  error bikeId:", in.EbikeId, " data:", utils.ToJsonString(value))
		return
	}

	event.Emit(&event.Data{
		Type: event.Event_Bike_Cmd,
		Data: &event.BikeCmd{
			EbikeId: in.EbikeId,
			CmdType: "toggle",
			Body:    value,
		},
	})
	utils.ResponseOk(c, nil)
	return
}

// SendMsgToDevice 发送信息给设备端
func (s *signal) SendMsgToDevice(c *gin.Context) {
	var (
		in  = &SignalPo.MessageReq{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	//  查询sn码
	sn, err := bll.Ebike.GetSn(in.EbikeId)
	if err != nil {
		c.Error(fmt.Errorf("get sn code failed"))
		return
	}

	// 执行指令
	in.Sn = sn
	value := (*dto.SettingRequest)(unsafe.Pointer(uintptr(unsafe.Pointer(in)) + uintptr(8)))

	trace_log.DebugLogger(sn, "SendMsgToDevice bikeId:", in.EbikeId, " data:", utils.ToJsonString(value))
	ctx, cancel := context.WithTimeout(c, 15*time.Second)
	defer cancel()
	err = bll.RpcCall(sn, func(addr string, err error) error {
		if err != nil {
			return err
		}

		_, err = grpc.Cmd.Setting(ctx, value, func(options *client.CallOptions) {
			options.Address = []string{addr}
		})
		return err
	})

	if err != nil {
		c.Error(s.FilterError(err))
		fmt.Println("SendMsgToDevice error bikeId:", in.EbikeId, " data:", utils.ToJsonString(value))
		return
	}

	event.Emit(&event.Data{
		Type: event.Event_Bike_Cmd,
		Data: &event.BikeCmd{
			EbikeId: in.EbikeId,
			CmdType: "set",
			Body:    value,
		},
	})
	utils.ResponseOk(c, nil)
	return
}

// GetMsgFromDevice 查询设备信息
func (s *signal) GetMsgFromDevice(c *gin.Context) {
	var (
		in  = &SignalPo.GetMessageReq{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	//  查询sn码
	sn, err := bll.Ebike.GetSn(in.EbikeId)
	if err != nil {
		c.Error(fmt.Errorf("get sn code failed"))
		return
	}

	switch in.Cmd {
	case "Battery":
		// 查询设备影子
		shadowRsp, _ := grpc.SCmd.GetShadowAttributes(c, &dto.GetShadowAttributesRequest{
			DeviceName: sn,
		})

		if shadowRsp == nil {
			c.Error(errors.DeviceCommandFailed.Error())
			return
		}

		var attribute model.ShadowAttribute
		if shadowRsp.Attribute != "" {
			err = json.Unmarshal([]byte(shadowRsp.Attribute), &attribute)
			if err != nil {
				c.Error(err)
				return
			}
			fmt.Printf("%s got battery attributes:%v \n", sn, attribute)
		} else {
			data, _err := bll.SignalV2.GetMsgFromDevice("", in)
			if _err != nil {
				c.Error(_err)
				return
			}
			fmt.Printf("%s got battery attributes req:%v \n", sn, in)
			fmt.Printf("%s got battery attributes:%v \n", sn, data)
			utils.ResponseOk(c, data)
			return
		}

		utils.ResponseOk(c, &dto.BatteryReply{
			Soc: int32(utils.StrToInt64(attribute.Soc) / 10), // 设备给出的电量单位为0.1%
		})
		return
	case "Location":
		ctx, cancel := context.WithTimeout(c, 30*time.Second)
		defer cancel()
		var ret *dto.Location
		err = bll.RpcCall(sn, func(addr string, err error) error {
			if err != nil {
				return err
			}
			ret, err = grpc.Cmd.Location(ctx, &dto.LocationRequest{Sn: sn}, func(options *client.CallOptions) {
				options.Address = []string{addr}
			})
			return err
		})

		if err != nil {
			c.Error(errors.DeviceCommandFailed.Error())
			fmt.Println("GetMsgFromDevice Location error bikeId:", in.EbikeId, " data:", sn, " err:", err)
			return
		}

		var location *po.Point
		if ret == nil {
			location = &po.Point{}
		}

		location = &po.Point{
			X: float64(ret.Lng) / 1000000.0,
			Y: float64(ret.Lat) / 1000000.0,
		}

		trace_log.DebugLogger(sn, "GetMsgFromDevice Location success location:", utils.ToJsonString(location))

		// 无信号
		if location.X == 361 && location.Y == 361 {
			c.Error(errors.NoGpsSignal.Error())
			return
		}

		// 同步到设备影子
		goroutine_pool.GetPool().Push(nil, func(data interface{}) error {
			_location, _ := json.Marshal(location)
			broker.Consumer.SyncDeviceInfoToShadow(sn, dto.EnUpdateType_ATTRIBUTE, map[string]interface{}{
				"location": string(_location),
			})
			return nil
		})

		utils.ResponseOk(c, location)
		return
	case "Controller":
		ret, err := s.getControllerInfo(c, sn)
		if err != nil {
			c.Error(errors.DeviceCommandFailed.Error())
			return
		}

		trace_log.DebugLogger(sn, "GetMsgFromDevice Controller success controller:", utils.ToJsonString(ret))
		data, _ := utils.TransProtoToJson(ret)
		utils.ResponseOk(c, data)
		return
	}

	c.Error(errors.New(fmt.Sprintf("not exist rpc handler name:%v", in.Cmd)))
	return
}

// getControllerInfo 查询设备信息
func (s *signal) getControllerInfo(ctx context.Context, sn string) (*dto.ControllerReply, error) {
	_ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
	defer cancel()
	// 执行指令
	var ret *dto.ControllerReply
	err := bll.RpcCall(sn, func(addr string, err error) error {
		if err != nil {
			return err
		}
		ret, err = grpc.Cmd.Controller(_ctx, &dto.ControllerRequest{Sn: sn}, func(options *client.CallOptions) {
			options.Address = []string{addr}
		})
		return err
	})

	if err != nil {
		fmt.Println("GetMsgFromDevice Controller error data:", sn, " err:", err)
		return nil, errors.DeviceCommandFailed.Error()
	}

	if ret == nil {
		ret = &dto.ControllerReply{}
	} else {
		if ret.CcuMac != "" {
			// 同步电池属性
			goroutine_pool.GetPool().Push(nil, func(d interface{}) error {
				defer utils.ExceptionCatch()
				if err != nil || ret == nil {
					return nil
				}

				// 同步设备信息到设备影子
				broker.Consumer.SyncDeviceInfoToShadow(sn, dto.EnUpdateType_ATTRIBUTE, map[string]interface{}{
					"ccu_mac":   ret.CcuMac,
					"sim_imei":  ret.SimImei,
					"sim_imsi":  ret.SimImsi,
					"sim_iccid": ret.SimIccid,
				})

				// 发射事件
				event.Emit(&event.Data{
					Type: event.Event_Control_Report,
					Data: &event.ControlReport{
						SnCode: sn,
						Data:   ret,
					},
				})
				return nil
			})
		}
	}
	return ret, nil
}

func (s *signal) reactiveCmd(c *gin.Context) {
	var (
		err error
		in  = &dto.ReactiveRequest{}
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if err = bll.Signal.ReactiveCmd(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, nil)
}

func (s *signal) FilterError(err error) error {
	if err == nil {
		return nil
	}

	if strings.Contains(err.Error(), "go.micro.client") {
		type Error struct {
			Detail string `json:"detail"`
		}

		var ret Error
		if _err := json.Unmarshal([]byte(err.Error()), &ret); _err != nil {
			return err
		}
		return fmt.Errorf(ret.Detail)
	}
	return err
}
